import math
import random
import threading
import time
from typing import Tuple, Union, Dict, List
import pygame
from pygame import Vector2
import math2 as m2
import config
import ui
import ai
from sound import Sound
from speak import Speaker


MAX_HOUSE_COUNT = 4


class Map:
  def __init__(
      self, game: 'Game', mid: int, title: str,
      pos: Tuple[float, float], size: Tuple[float, float],
      bgImgPath: str, sourceMapId: int = -1,
      cost: int = -1, areaColor: Tuple[int] = (255, 255, 255),
      rot: float = 0.0, drawSourceSurfaceName: str = 'map',
      payMoneyConfig=None, buildCostConfig=None,
  ):
    if buildCostConfig is None:
      buildCostConfig = {'house': 100, 'hotel': 1000}
    if payMoneyConfig is None:
      payMoneyConfig = {}
    self.game = game
    self.mid = mid
    self.buildCostConfig = buildCostConfig
    self.sourceMapId = sourceMapId
    self.areaColor = areaColor
    self.drawSourceSurfaceName = drawSourceSurfaceName
    self.rot = rot
    self.payMoneyConfig = payMoneyConfig
    self.buildNumber = {'house': 0, 'hotel': 0}
    self._ownerId = -1
    if self.sourceMapId >= 0:
      self.sourceMap = self.game.map[sourceMapId]
    elif self.sourceMapId == -2:
      self.sourceMap = self.game.map[-1]
    else:
      self.sourceMap = None
    self._pos = pos
    self.size = size
    self.title = title
    self.cost = cost
    self._mortgage = False
    """抵押"""
    self.bgImgPath = bgImgPath
    self.bgImg = pygame.image.load(bgImgPath).convert()

    self.bgSurface = None
    self.panel = ui.Img(
      imgPath=self.bgImgPath,
      pos=self.leftPos, size=self.size, rot=self.rot,
      childPanelDict={
        'fgImg@Img': {
          'imgPath': 'texture/frame_1.png',
          'imgGrid': [1, 1, 1, 1],
          'imgColor': (0, 0, 0),
          'size': self.size,
          'layer': 1,
        }
      }
    )
    if self.cost != -1:
      self.panel.AddChildNode('buildBgImg', ui.Img(
        imgPath='texture/white.png', imgColor=self.areaColor,
        size=(self.size[0], int(self.size[1] * 0.25)),
        childPanelDict={
          'buildFgImg@Img': {
            'imgPath': 'texture/frame_1.png',
            'imgGrid': [1, 1, 1, 1],
            'imgColor': (0, 0, 0),
            'size': (self.size[0], int(self.size[1] * 0.25)),
          },
        }
      ))
      self.panel.AddChildNode('diYaImg', ui.Img(
        imgPath='texture/di_ya.png', size=self.size, layer=20, visible=False
      ))
      self.panel.AddChildNode('cityName', ui.Text(
        text=self.title, textSize=self.fontSize, textColor=(0, 0, 0), textAlign=ui.TextAlignEnum.RIGHT,
        pos=self.titlePos, layer=2, size=(self.size[0] - 4, self.fontSize + 4),
        textFont='font/JingNanMaiYuanTi-2.otf',
      ))
      self.panel.AddChildNode('cityValue', ui.Text(
        text='${}'.format(self.cost), textSize=18, textColor=(218, 165, 32), textAlign=ui.TextAlignEnum.RIGHT,
        pos=(0, self.size[1] - 28), layer=2, size=(self.size[0] - 4, 22),
        textFont='font/JingNanMaiYuanTi-2.otf',
      ))
      self.panel.AddChildNode('ownerId', ui.Text(
        text='', textSize=56, textColor=(0, 0, 0), textAlign=ui.TextAlignEnum.CENTER,
        pos=(0, (self.size[1] - 56) * 0.5), layer=3, size=(self.size[0] - 4, 56),
        textFont='font/JingNanMaiYuanTi-2.otf',
      ))
      self.panel.AddChildNode('house0', ui.Btn(
        upFunc=self.PayHouse,
        defImgPath='texture/house.png', hoverImgPath='texture/house.png', pressImgPath='texture/house.png',
        pos=(5, 5), layer=5, size=(32, 32), visible=False
      ))
      self.panel.AddChildNode('house1', ui.Btn(
        upFunc=self.PayHouse,
        defImgPath='texture/house.png', hoverImgPath='texture/house.png', pressImgPath='texture/house.png',
        pos=(37, 5), layer=5, size=(32, 32), visible=False
      ))
      self.panel.AddChildNode('house2', ui.Btn(
        upFunc=self.PayHouse,
        defImgPath='texture/house.png', hoverImgPath='texture/house.png', pressImgPath='texture/house.png',
        pos=(5, 37), layer=5, size=(32, 32), visible=False
      ))
      self.panel.AddChildNode('house3', ui.Btn(
        upFunc=self.PayHouse,
        defImgPath='texture/house.png', hoverImgPath='texture/house.png', pressImgPath='texture/house.png',
        pos=(37, 37), layer=5, size=(32, 32), visible=False
      ))
      self.panel.AddChildNode('hotel', ui.Btn(
        upFunc=self.PayHotel,
        defImgPath='texture/hotel.png', hoverImgPath='texture/hotel.png', pressImgPath='texture/hotel.png',
        pos=(5, 5), layer=5, size=(64, 48), visible=False
      ))
      self.panel.AddChildNode('diYaBtn', ui.Btn(
        textSize=20, text='抵押', textColor=(240, 0, 0),
        upFunc=self.DiYa, imgColor=(240, 0, 0), imgGrid=[1, 1, 1, 1],
        pos=(5, self.size[1] * 0.25 + 5), layer=5, size=(64, 48), visible=False
      ))
      self.panel.AddChildNode('sellBtn', ui.Btn(
        textSize=20, text='出售', textColor=(240, 0, 0),
        upFunc=self.SellSelf, imgColor=(240, 0, 0), imgGrid=[1, 1, 1, 1],
        pos=(5, self.size[1] * 0.25 + 5), layer=21, size=(64, 48), visible=False
      ))
    else:
      self.panel.AddChildNode('cityName', ui.Text(
        text=self.title, textSize=self.fontSize, textColor=(0, 0, 0), textAlign=ui.TextAlignEnum.RIGHT,
        pos=self.titlePos, layer=2, size=(self.size[0] - 4, self.fontSize + 4),
        textFont='font/JingNanMaiYuanTi-2.otf',
      ))

  def SellSelf(self, btn):
    addMoney = int(
      self.cost * 0.5
    ) + int(
      self.buildNumber['house'] * self.buildCostConfig['house'] * 0.5
    ) + int(
      self.buildNumber['hotel'] * self.buildCostConfig['hotel'] * 0.5
    )
    self.owner.money += addMoney
    self.mortgage = False
    self.buildNumber = {'house': 0, 'hotel': 0}
    self.game.Msg(
      '{} 卖出了{}，获得${}！（当前资产${}）'.format(
        self.owner.name, self.title, addMoney, self.owner.money
      )
    )
    self.ownerId = -1

  def DiYa(self, btn):
    self.mortgage = True
    self.owner.money += int(self.cost * 0.5)
    self.game.Msg(
      '{} 以{}作为抵押获得${}！（当前资产${}）'.format(
        self.owner.name, self.title, int(self.cost * 0.5), self.owner.money
      )
    )

  def PayHouse(self, btn):
    """贩卖旅馆"""
    if self.game.sellingMode != self.ownerId or self.ownerId == -1:
      return
    if self.buildNumber['house'] < 1:
      return
    self.buildNumber['house'] -= 1
    addMoney = int(self.buildCostConfig['house'] * 0.5)
    self.owner.money += addMoney
    self.game.Msg('{} 贩卖了 {} 的旅馆，获得${}！（当前资产${}）'.format(self.owner.name, self.title, addMoney, self.owner.money))

  def PayHotel(self, btn):
    """贩卖酒店"""
    if self.game.sellingMode != self.ownerId or self.ownerId == -1:
      return
    if self.buildNumber['hotel'] < 1:
      return
    self.buildNumber = {'house': MAX_HOUSE_COUNT, 'hotel': 0}
    addMoney = int(self.buildCostConfig['hotel'] * 0.5)
    self.owner.money += addMoney
    self.game.Msg('{} 贩卖了 {} 的酒店，获得${}！（当前资产${}）'.format(self.owner.name, self.title, addMoney, self.owner.money))

  @property
  def mortgage(self):
    """抵押"""
    return self._mortgage

  @mortgage.setter
  def mortgage(self, value):
    self._mortgage = value
    self.panel.childPanel['diYaImg'].visible = value

  @property
  def ownerId(self):
    return self._ownerId

  @ownerId.setter
  def ownerId(self, value):
    # print('ownerId=', value)
    self._ownerId = value
    if value == -1:
      self.panel.childPanel['ownerId'].text = ''
      self.mortgage = False
      self.buildNumber = {'house': 0, 'hotel': 0}
    else:
      self.panel.childPanel['ownerId'].text = str(value)
      self.panel.childPanel['ownerId'].textColor = self.owner.bgColor

  def JudgeAllColorIsOne(self):
    for _map in self.game.map:
      if _map.areaColor == self.areaColor and _map.ownerId != self.ownerId:
        return False
    return True

  @property
  def payMoney(self):
    """过路费"""
    if sum(self.buildNumber.values()) == 0:
      if self.JudgeAllColorIsOne():
        return self.payMoneyConfig.get('payMoney', 0) * 2
      return self.payMoneyConfig.get('payMoney', 0)
    return self.payMoneyConfig.get('house', {}).get(self.buildNumber['house'], 0) + self.payMoneyConfig.get('hotel', {}).get(self.buildNumber['hotel'], 0)

  def GetNextBuildInfo(self):
    """获取修建房子的信息"""
    buildNumber = self.buildNumber.copy()
    if buildNumber['house'] >= MAX_HOUSE_COUNT:
      buildNumber = {'house': 0, 'hotel': 1}
      return self.buildCostConfig.get('hotel', 0), '酒店', self.payMoneyConfig.get('hotel', {}).get(buildNumber['hotel'], 0)
    buildNumber['house'] += 1
    return self.buildCostConfig.get('house', 0), '旅馆', self.payMoneyConfig.get('house', {}).get(buildNumber['house'], 0)

  @property
  def canOnPlayerSize(self):
    if self.cost != -1:
      # return self.size[0], self.size[1] * 0.75
      return self.size[0], self.size[1]
    return self.size

  @property
  def sourceSurface(self):
    return self.game.GetSurface(self.drawSourceSurfaceName)

  @property
  def fontSize(self):
    if len(self.title) > 4:
      return 13
    if len(self.title) > 3:
      return 16
    return 22

  def Update(self):
    if self.cost > 0:
      for i in range(4):
        buildName = 'house{}'.format(i)
        self.panel.childPanel[buildName].visible = i < self.buildNumber['house']
      self.panel.childPanel['hotel'].visible = self.buildNumber['hotel'] > 0
      self.panel.childPanel['diYaBtn'].visible = self.game.sellingMode == self.ownerId and self.ownerId != -1 and not self.mortgage
      self.panel.childPanel['sellBtn'].visible = self.game.sellingMode == self.ownerId and self.ownerId != -1 and self.mortgage
      # self.panel.childPanel['diYaBtn'].visible = True

  @property
  def owner(self):
    return self.game.player[self.ownerId]

  @property
  def titlePos(self):
    if self.cost == -1:
      return m2.TupleAdd(self.downPos, (2, -28))
    return m2.TupleAdd(self.downPos, (2, -52))

  @property
  def leftPos(self):
    if self.sourceMap is None:
      return self._pos
    return m2.TupleAdd(self.sourceMap.leftPos, self._pos)

  @property
  def downPos(self):
    return 0, self.size[1]


class Projection:
  """玩家在棋盘上的投影"""
  def __init__(self, player: 'Player'):
    self.player = player
    self.game = player.game
    self.size = (24, 48)
    # self.pos = (75 * 0.5 * (self.player.pid % 2), 150 * 0.25 * (self.player.pid // 2))
    self.pos = (27 * (self.player.pid % 2), 142 * 0.125 * self.player.pid + 2)
    # print('self.pos=', self.pos)
    self._mapPos = 0
    self.panel = ui.Img(
      imgPath='texture/player.png', imgColor=self.player.bgColor,
      size=self.size, pos=self.pos, layer=30, rot=90,
    )
    self.panelName = 'player{}Projection'.format(self.player.pid)
    self.img = pygame.image.load('texture/player.png').convert_alpha()
    # self.img.fill(player.bgColor)
    self.ChangeImgColor()
    self.surface = None  # type: Union[None, pygame.Surface]
    self.mapPos = 0

  @property
  def mapPos(self):
    return self._mapPos

  @mapPos.setter
  def mapPos(self, value):
    self.game.map[self.mapPos].panel.RemoveChildNode(self.panelName)
    self._mapPos = value
    self.game.map[self.mapPos].panel.AddChildNode(self.panelName, self.panel)
    # self.game.mapPanel.pos = (
    #   self.game.displaySize[0] * 0.5 - self.onMap.leftPos[0], self.game.displaySize[1] * 0.5 - self.onMap.leftPos[1]
    # )
    self.game.gotoMapPos = (
      self.game.displaySize[0] * 0.5 - self.onMap.leftPos[0], self.game.displaySize[1] * 0.5 - self.onMap.leftPos[1]
    )

  def ChangeImgColor(self):
    for x in range(self.img.get_width()):
      for y in range(self.img.get_height()):
        # 获取当前像素的颜色值
        pixel_color = self.img.get_at((x, y))

        # 检查当前像素是否透明
        if pixel_color[3] > 0:
          # 应用颜色滤波器，同时保持透明度
          new_pixel_color = (self.player.bgColor[0], self.player.bgColor[1], self.player.bgColor[2], pixel_color[3])
          self.img.set_at((x, y), new_pixel_color)

  @property
  def sourceSurface(self):
    return self.game.GetSurface('map')

  @property
  def onMap(self) -> Map:
    return self.game.map[self.mapPos]

  @property
  def center(self) -> Tuple[float, float]:
    leftPos = self.onMap.leftPos
    # size = m2.TupleSub(self.onMap.size, self.size)
    size = self.onMap.canOnPlayerSize
    localPos = m2.TupleMul(size, self.pos)
    localPos = m2.TupleAdd(localPos, (4, 4))
    return m2.TupleAdd(leftPos, localPos)

  def Draw(self):
    if self.surface is None:
      self.surface = pygame.Surface(self.size, pygame.SRCALPHA)

      # pygame.draw.circle(self.surface, self.player.bgColor, (0, 0), self.size[0] * 0.5)
      # pygame.draw.rect(self.surface, self.player.bgColor, (0, 0, self.size[0], self.size[1]))
      img = pygame.transform.scale(self.img, self.size)
      self.surface.blit(img, (0, 0))

    surface = pygame.transform.rotate(self.surface, self.onMap.rot + 90)
    self.sourceSurface.blit(surface, self.center)


class Player:
  def __init__(
      self, game: 'Game', pid: int, pos: Tuple[Union[int, float, str]], size: Tuple[int, int],
      bgColor: Tuple, fgColor: Tuple, rot: int = 0, money: int = 15000
  ):
    self.game = game
    self.pid = pid
    self.rot = rot
    self._pos = pos
    self.size = size
    self.money = money
    self.bgColor = bgColor
    self.fgColor = fgColor
    self.offset = (0, 0)
    self.ai = ai.Ai(self)

    self.panel = ui.Img(
      pos=self.pos, size=self.size, rot=self.rot,
      imgPath='texture/white.png', imgColor=self.bgColor,
      inResult=ui.InResultEnum.OCCLUSION, canDrag=True,
      childPanelDict={
        # 玩家面板的子控件：玩家名、金钱、弹窗、骰子按钮、胜负图层
        'playerName@Text': {
          'text': '{}'.format(self.name),
          'textSize': 22,
          'textShadow': True,
          'textFont': 'font/JingNanMaiYuanTi-2.otf',
          'textColor': self.fgColor,
          'textAlign': ui.TextAlignEnum.LEFT,
          'size': (180, 22),
          'pos': (5, 5),
          'layer': 1,
        },
        'playerMoney@Text': {
          'text': '${}'.format(self.money),
          'textSize': 40,
          'textShadow': True,
          'textFont': 'font/JingNanMaiYuanTi-2.otf',
          'textColor': (255, 215, 0),
          'textAlign': ui.TextAlignEnum.LEFT,
          'size': (180, 40),
          'pos': (5, 24),
          'layer': 1,
        },
        'playerMap@Text': {
          'text': '地皮：0块',
          'textSize': 20,
          'textShadow': True,
          'textFont': 'font/JingNanMaiYuanTi-2.otf',
          'textColor': (255, 255, 255),
          'textAlign': ui.TextAlignEnum.LEFT,
          'size': (180, 20),
          'pos': (5, 65),
          'layer': 1,
        },
        'playerHouse@Text': {
          'text': '旅馆：0个',
          'textSize': 20,
          'textShadow': True,
          'textFont': 'font/JingNanMaiYuanTi-2.otf',
          'textColor': (255, 255, 255),
          'textAlign': ui.TextAlignEnum.LEFT,
          'size': (180, 20),
          'pos': (5, 85),
          'layer': 1,
        },
        'playerHotel@Text': {
          'text': '酒店：0个',
          'textSize': 20,
          'textShadow': True,
          'textFont': 'font/JingNanMaiYuanTi-2.otf',
          'textColor': (255, 255, 255),
          'textAlign': ui.TextAlignEnum.LEFT,
          'size': (180, 20),
          'pos': (5, 105),
          'layer': 1,
        },
        'fgImg@Img': {
          'imgPath': 'texture/frame_1.png',
          'imgColor': self.fgColor,
          'imgGrid': [1, 1, 1, 1],
          'size': self.size,
        },
        'rollBtn@Btn': {
          'defImgPath': 'texture/p_r.png',
          'hoverImgPath': 'texture/p_r_hover.png',
          'pressImgPath': 'texture/p_r_press.png',
          'size': (50, 50),
          'pos': (self.size[0] - 55, (self.size[1] - 50) * 0.5),
          'upFunc': self.Roll,
          'layer': 1,
          'visible': False,
          'downSound': 'sound/btndown_04.wav',
        },
        'msgImg@Img': {
          'imgPath': 'texture/white.png',
          'imgColor': self.bgColor,
          'size': self.size,
          'layer': 2,
          'visible': False,
          'childPanelDict': {
            'msgFgImg@Img': {
              'imgPath': 'texture/frame_2.png',
              'imgColor': self.fgColor,
              'imgGrid': [2, 2, 2, 2],
              'size': self.size,
            },
            'msgText0@Text': {
              'text': '弹窗内容',
              'textSize': 18,
              'textShadow': True,
              'textFont': 'font/JingNanMaiYuanTi-2.otf',
              'textColor': self.fgColor,
              'textAlign': ui.TextAlignEnum.CENTER,
              'size': (180, 18),
              'pos': (5, 10),
              'layer': 1,
            },
            'msgText1@Text': {
              'text': '弹窗内容2',
              'textSize': 18,
              'textShadow': True,
              'textFont': 'font/JingNanMaiYuanTi-2.otf',
              'textColor': self.fgColor,
              'textAlign': ui.TextAlignEnum.CENTER,
              'size': (180, 18),
              'pos': (5, 34),
              'layer': 1,
            },
            'msgText2@Text': {
              'text': '弹窗内容',
              'textSize': 18,
              'textShadow': True,
              'textFont': 'font/JingNanMaiYuanTi-2.otf',
              'textColor': self.fgColor,
              'textAlign': ui.TextAlignEnum.CENTER,
              'size': (180, 18),
              'pos': (5, 58),
              'layer': 1,
            },
            'msgText3@Text': {
              'text': '弹窗内容2',
              'textSize': 18,
              'textShadow': True,
              'textFont': 'font/JingNanMaiYuanTi-2.otf',
              'textColor': self.fgColor,
              'textAlign': ui.TextAlignEnum.CENTER,
              'size': (180, 18),
              'pos': (5, 82),
              'layer': 1,
            },
            'msgLeftBtn@Btn': {
              'text': '左按钮',
              'textColor': self.fgColor,
              'textFont': 'font/JingNanMaiYuanTi-2.otf',
              'defImgPath': 'texture/btn_def.png',
              'hoverImgPath': 'texture/btn_hover.png',
              'pressImgPath': 'texture/btn_press.png',
              'imgGrid': [1, 1, 1, 1],
              'size': (60, 30),
              'pos': (10, self.size[1] - 40),
              'layer': 1,
              'downSound': 'sound/btndown_no.wav',
            },
            'msgRightBtn@Btn': {
              'text': '右按钮',
              'textColor': self.fgColor,
              'textFont': 'font/JingNanMaiYuanTi-2.otf',
              'defImgPath': 'texture/btn_def.png',
              'hoverImgPath': 'texture/btn_hover.png',
              'pressImgPath': 'texture/btn_press.png',
              'imgGrid': [1, 1, 1, 1],
              'size': (60, 30),
              'pos': (self.size[0] - 70, self.size[1] - 40),
              'layer': 1,
              'downSound': 'sound/btndown_yes.wav',
            }
          }
        },
        'poChan@Img': {
          'imgPath': 'texture/po_chan.png',
          'size': self.size,
          'layer': 20,
          'visible': False,
        },
        'aiBtn@Btn': {
          'pos': (self.size[0] - 18, self.size[1] - 39),
          'size': (16, 16),
          'defImgPath': 'texture/robot_def.png',
          'hoverImgPath': 'texture/robot_hover.png',
          'pressImgPath': 'texture/robot_press.png',
          'upFunc': self.EnableAi,
          'layer': 10,
          'downSound': 'sound/btndown_05.wav',
          'childPanelDict': {
            'banImg@Img': {
              'size': (16, 16),
              'imgPath': 'texture/ban.png',
            }
          }
        },
      }
    )
    self.banImg = self.panel.childPanel['aiBtn'].childPanel['banImg']
    # print('player panel=', self.panel)
    self.panel.AddChildNode('rotBtn', ui.Btn(
      pos=(self.size[0] - 18, self.size[1] - 18),
      size=(16, 16),
      defImgPath='texture/rot.png',
      hoverImgPath='texture/rot_hover.png',
      pressImgPath='texture/rot_press.png',
      upFunc=self.RotNext,
      layer=10,
      downSound='sound/btndown_05.wav'
    ))

    self.projection = Projection(self)

    self.baseSurface = None  # type: Union[None, pygame.Surface]
    self.lastSurface = None  # type: Union[None, pygame.Surface]

    self.canRollBtn = self.panel.childPanel['rollBtn']
    self.playerName = self.panel.childPanel['playerName']
    self.moneyText = self.panel.childPanel['playerMoney']
    self.msgPanel = self.panel.childPanel['msgImg']
    self.msgBoxLeftBtn = self.msgPanel.childPanel['msgLeftBtn']
    self.msgBoxRightBtn = self.msgPanel.childPanel['msgRightBtn']

    self.roll = Roll(self.game, self)
    # self.msgBoxText = self.msgPanel.childPanel['msgText']
    # self.msgBoxText2 = self.msgPanel.childPanel['msgText2']
    # print('player msgBoxText=', self.msgBoxText, self.msgBoxText.sourcePanel, self.msgBoxText.sourcePanel.sourcePanel)

    self.readyMove = (0, 0)

    self._fail = False

  def EnableAi(self, btn):
    self.banImg.visible = not self.banImg.visible

  @property
  def name(self):
    return config.playerNameConfig[self.pid]

  def SetMapInfo(self):
    allMap = self.GetAllProperty()
    allBuild = self.GetAllBuild()
    self.panel.childPanel['playerMap'].text = '地皮：{}块'.format(len(allMap))
    self.panel.childPanel['playerHouse'].text = '旅馆：{}个'.format(allBuild['house'])
    self.panel.childPanel['playerHotel'].text = '酒店：{}个'.format(allBuild['hotel'])

  def LoseMoney(self, value, text='资金不足，点击你的房屋半价出售或地皮抵押/出售', finishedFunc=None, giveUpFunc=None):
    def GiveUp(btn):
      self.CloseMsgBox()
      self.game.sellingMode = -1
      self.fail = True
      if giveUpFunc is not None:
        self.game.AddTimer(2, giveUpFunc)

    def Finished(btn):
      self.CloseMsgBox()
      self.game.sellingMode = -1
      if self.LoseMoney(value, text, finishedFunc):
        if finishedFunc is not None:
          finishedFunc()

    if self.fail:
      return True
    if self.money >= value:
      print('{} 支付了 ${}'.format(self.name, value))
      self.money -= value
      self.game.failMusic.play()
      return True
    if not self.GetAllProperty():
      self.fail = True
      return True
    # 需要抵押物品或者贩卖建筑
    self.game.sellingMode = self.pid
    self.MsgBox(
      text,
      leftBtnText='认输',
      rightBtnText='完毕',
      leftDownFunc=GiveUp,
      rightDownFunc=Finished
    )
    return False

  @property
  def fail(self):
    return self._fail

  @fail.setter
  def fail(self, value):
    self._fail = value
    if value:
      self.game.Msg('{} 破产！！！'.format(self.name))
      self.panel.childPanel['poChan'].visible = True
      for _map in self.game.map:
        if _map.ownerId == self.pid:
          _map.ownerId = -1
          _map.mortgage = False
          _map.buildNumber = {'house': 0, 'hotel': 0}
      noFails = self.game.GetNoFailedPlayers()
      if len(noFails) == 1:
        self.game.gameOver = True
        self.game.Winner('{} 成为大富翁！！！'.format(noFails[0].name))

  def RotNext(self, btn):
    self.panel.rot = (self.panel.rot + 90) % 360

  def GetAllProperty(self) -> List[Map]:
    """获取所有资产"""
    result = []
    for _map in self.game.map:
      if _map.ownerId == self.pid:
        result.append(_map)
    return result

  def GetAllBuild(self) -> Dict[str, int]:
    """获取所有建筑"""
    result = {'house': 0, 'hotel': 0}
    for _map in self.game.map:
      if _map.ownerId == self.pid:
        result['house'] += _map.buildNumber['house']
        result['hotel'] += _map.buildNumber['hotel']
    return result

  def MsgBox(self, text, leftBtnText='否', rightBtnText='是', leftDownFunc=None, rightDownFunc=None):
    def SetText():
      # if len(text) > 24:
      #   self.msgBoxText.text = text[:12]
      #   self.msgBoxText2.text = text[12:]
      #   self.msgBoxText.textSize = 12
      #   self.msgBoxText2.textSize = 12
      #   return
      # if len(text) > 18:
      #   self.msgBoxText.text = text[:10]
      #   self.msgBoxText2.text = text[10:]
      #   self.msgBoxText.textSize = 14
      #   self.msgBoxText2.textSize = 14
      #   return
      # self.msgBoxText.text = text[:9]
      # self.msgBoxText2.text = text[9:]
      # self.msgBoxText.textSize = 16
      # self.msgBoxText2.textSize = 16
      lineCount = math.ceil(len(text) / 12)
      for lineId in range(lineCount):
        self.msgPanel.childPanel['msgText{}'.format(lineId)].text = text[lineId*12:lineId*12+12]
      for lineId in range(lineCount, 4):
        self.msgPanel.childPanel['msgText{}'.format(lineId)].text = ''

    SetText()
    self.msgBoxLeftBtn.upFunc = leftDownFunc
    self.msgBoxRightBtn.upFunc = rightDownFunc
    self.msgBoxLeftBtn.text = leftBtnText
    self.msgBoxRightBtn.text = rightBtnText
    self.msgPanel.visible = True
    # self.panel.ClearSurfaceTemp()
    self.game.dingMusic.play()

  def CloseMsgBox(self):
    # self.msgBoxText.text = ''
    self.msgPanel.visible = False

  def Roll(self, btn):
    print('玩家{}Roll'.format(self.pid))
    self.roll.Start()

  @property
  def pos(self):
    def GetValue(v, p, s):
      if type(v) == str and 'p' in v:
        return eval(v, {'p': p, 's': s})
      return v

    return GetValue(
      self._pos[0], self.game.screen.get_width(), self.size[0]
    ) + self.offset[0], GetValue(
      self._pos[1], self.game.screen.get_height(), self.size[1]
    ) + self.offset[1]

  def SetPlayerNameSize(self):
    # print('len(self.playerName.text)=', len(self.playerName.text))
    if len(self.projection.onMap.title) > 4:
      self.playerName.textSize = 16
    elif len(self.projection.onMap.title) > 2:
      self.playerName.textSize = 18
    else:
      self.playerName.textSize = 20

  def Draw(self):
    self.moneyText.text = '${}'.format(self.money)
    self.playerName.text = '{} —— {}'.format(self.name, self.projection.onMap.title)
    self.SetMapInfo()
    self.SetPlayerNameSize()
    self.panel.Draw(self.game.screen)
    self.roll.Draw()

    if not self.banImg.visible:
      self.ai.Op(self.game.flowPath)


class FlowPath:
  def __init__(self, game: 'Game'):
    self.game = game
    self.flowPaths = [0, 0]
    """当前游戏的流程：0: 第N位玩家操作；1: 第N个流程"""
    self.nowFlowPathFinished = True
    self.lotteryFuncInfo = None
    self.fangChanShui = 0
    self.stopPlayer = {}
    self.dontPayPlayer = set()

  @property
  def nowFlowPathName(self):
    return config.flowPathConfig[self.flowPaths[1] - 1]

  @property
  def nowPlayer(self):
    return self.game.player[self.flowPaths[0]]

  def Roll(self):
    self.nowPlayer.canRollBtn.visible = True

  def Buy(self):
    def Yes(btn):
      self.nowPlayer.CloseMsgBox()
      self.nowPlayer.projection.onMap.ownerId = self.nowPlayer.pid
      self.nowPlayer.money -= self.nowMap.cost
      self.DelayMsg(
        '{} 花费${} 购买了{}'.format(self.nowPlayer.name, self.nowMap.cost, self.nowMap.title), delay=1
      )

    def No(btn):
      self.nowPlayer.CloseMsgBox()
      self.nowFlowPathFinished = True

    if 0 < self.nowMap.cost <= self.nowPlayer.money and self.nowMap.ownerId == -1:
      # 提示是否购买
      self.game.Msg('{}决策是否购买 {}（${}）'.format(
        self.nowPlayer.name, self.nowMap.title, self.nowMap.cost
      ))
      self.nowPlayer.MsgBox(
        '现有${}，是否花${}购买 {}？（空地过路费${}）'.format(
          self.nowPlayer.money,
          self.nowPlayer.projection.onMap.cost,
          self.nowPlayer.projection.onMap.title,
          self.nowPlayer.projection.onMap.payMoneyConfig.get('payMoney', '?')
        ),
        leftDownFunc=No,
        rightDownFunc=Yes
      )
      return
    self.nowFlowPathFinished = True

  def FlowPathFinished(self):
    self.nowFlowPathFinished = True

  def DelayMsg(self, text, delay=2):
    print('DelayMsg=', text, delay)
    self.game.Msg(
      text, delay, self.FlowPathFinished
    )

  def BuyBack(self):

    def Yes(btn):
      self.nowPlayer.CloseMsgBox()
      self.nowPlayer.money -= needCost
      self.nowMap.mortgage = False
      self.DelayMsg(
        '{} 花费${} 赎回了{}'.format(self.nowPlayer.name, needCost, self.nowMap.title),
      )

    def No(btn):
      self.nowPlayer.CloseMsgBox()
      self.nowFlowPathFinished = True

    needCost = int(self.nowMap.cost * 0.6)
    if self.nowMap.cost > 0 and self.nowPlayer.money >= needCost and self.nowMap.ownerId == self.nowPlayer.pid and self.nowMap.mortgage:
      # 提示是否赎回
      self.game.Msg('等待 {} 决策是否赎回 {}（${}）'.format(
        self.nowPlayer.name, self.nowMap.title, needCost
      ))
      self.nowPlayer.MsgBox(
        '是否赎回{}（${}）？'.format(self.nowMap.title, needCost),
        leftDownFunc=No, rightDownFunc=Yes
      )
      return
    self.nowFlowPathFinished = True

  def Move(self):
    def Do():
      newMapPos = self.nowPlayer.projection.mapPos + 1
      if newMapPos >= len(self.game.map):
        self.nowPlayer.projection.mapPos = 0
        self.nowPlayer.money += config.WAGES
        self.game.Msg('玩家路过起点，获得${}！'.format(config.WAGES))
      else:
        self.nowPlayer.projection.mapPos = newMapPos
      self.game.moveMusic.play()

    def End():
      self.nowFlowPathFinished = True
      self.nowPlayer.roll.tick -= 10

    for s in range(self.nowPlayer.roll.movePoint):
      self.game.AddTimer(s * 0.24, Do)

    self.game.AddTimer(self.nowPlayer.roll.movePoint * 0.24, End)

  def Build(self):
    """建房子"""
    def Yes(btn):
      self.nowPlayer.CloseMsgBox()
      self.nowPlayer.money -= buildCost
      if buildName == '旅馆':
        self.nowMap.buildNumber['house'] += 1
        self.game.buildHouseMusic.play()
      else:
        self.nowMap.buildNumber = {'house': 0, 'hotel': 1}
        self.game.buildHotelMusic.play()
      # self.nowMap.panel.ClearSurfaceTemp()
      self.DelayMsg('{} 花费${} 在 {} 修建了{}！'.format(
        self.nowPlayer.name, buildCost, self.nowMap.title, buildName)
      )

    def No(btn):
      self.nowPlayer.CloseMsgBox()
      self.nowFlowPathFinished = True

    if self.nowMap.mortgage:
      self.nowFlowPathFinished = True
      return
    if self.nowMap.ownerId == self.nowPlayer.pid and self.nowMap.buildNumber['hotel'] < 1:
      buildCost, buildName, payMoney = self.nowMap.GetNextBuildInfo()
      if buildCost > self.nowPlayer.money:
        self.nowFlowPathFinished = True
        return
      self.game.Msg('等待{}决策是否花费${}造{}，过路费{}……'.format(self.nowPlayer.name, buildCost, buildName, payMoney))
      self.nowPlayer.MsgBox(
        '当前持有${}，是否花费${}修建{}？（过路费${}）'.format(self.nowPlayer.money, buildCost, buildName, payMoney),
        leftDownFunc=No,
        rightDownFunc=Yes
      )
    else:
      self.nowFlowPathFinished = True

  def Lottery(self):
    """抽奖"""
    if self.nowMap.title not in config.lotteryConfig:
      self.nowFlowPathFinished = True
      return
    if self.lotteryFuncInfo is None:
      self.lotteryFuncInfo = random.choice(config.lotteryConfig[self.nowMap.title])
      self.game.Msg('{} 抽取到“{}”！'.format(self.nowPlayer.name, self.lotteryFuncInfo[0]))
    func = getattr(self, self.lotteryFuncInfo[1])
    result = func(*self.lotteryFuncInfo[2:])
    if result:
      self.game.AddTimer(3, self.FlowPathFinished)

  def GiveMoney(self, value):
    self.nowPlayer.money += value
    self.game.goldFallMusic.play()
    return True

  def LoseMoney(self, value, text='资金不足，点击你的房屋半价出售或地皮抵押/出售', finishedFunc=None):

    if finishedFunc is None:
      finishedFunc = self.FlowPathFinished
    return self.nowPlayer.LoseMoney(value, text, finishedFunc, self.FlowPathFinished)

  def LoseMulMoney(self, value):
    self.nowPlayer.money -= int(self.nowPlayer.money * value)
    print('{} 支付了 {}'.format(self.nowPlayer.name, int(self.nowPlayer.money * value)))
    self.game.failMusic.play()
    return True

  def JiuJi(self, value):
    for _player in self.game.player:
      if _player.fail:
        continue
      if _player.pid == self.nowPlayer.pid:
        continue
      newValue = min(_player.money, value)
      _player.money -= newValue
      self.nowPlayer.money += newValue
    self.game.goldFallMusic.play()
    return True

  def SetMapPos(self, mapPos):
    self.nowPlayer.projection.mapPos = mapPos
    self.flowPaths[1] = 2
    self.game.moveMusic.play()
    return True

  def MoveMapPos(self, distance):
    def Do():
      newMapPos = self.nowPlayer.projection.mapPos + 1
      if newMapPos >= len(self.game.map):
        self.nowPlayer.projection.mapPos = 0
        self.nowPlayer.money += config.WAGES
        self.game.Msg('玩家路过起点，获得${}！'.format(config.WAGES))
      else:
        self.nowPlayer.projection.mapPos = newMapPos
      self.game.moveMusic.play()
    for _ in range(distance):
      Do()
    self.flowPaths[1] = 2
    return True

  def MoveToMap(self, mapTitle, needPay=False):
    mapPos = self.game.GetMapPosByTitle(mapTitle)
    if self.nowPlayer.projection.mapPos > mapPos:
      self.nowPlayer.money += 2000
    self.nowPlayer.projection.mapPos = mapPos
    self.flowPaths[1] = 2
    if not needPay:
      self.dontPayPlayer.add(self.nowPlayer.pid)
    self.game.moveMusic.play()
    return True

  def StopMove(self, count):
    self.stopPlayer[self.nowPlayer.pid] = count
    self.game.failMusic.play()
    return True

  def FangChanShui(self, costConfig):
    allBuild = self.nowPlayer.GetAllBuild()
    if self.fangChanShui == 0:
      for k, v in costConfig.items():
        self.fangChanShui += int(allBuild.get(k, 0) * v)
      self.game.failMusic.play()
    return self.LoseMoney(self.fangChanShui)

  @property
  def nowMap(self):
    return self.nowPlayer.projection.onMap

  def Pay(self):
    def PayFinishedFunc():
      self.nowMap.owner.money += self.nowMap.payMoney
      self.DelayMsg('{} 向 {} 支付了${} 作为过路费'.format(
        self.nowPlayer.name, self.nowMap.owner.name, self.nowMap.payMoney
      ))

    if self.nowMap.cost > 0 and self.nowMap.ownerId not in [-1, self.nowPlayer.pid] and not self.nowMap.mortgage:
      if self.nowPlayer.pid in self.dontPayPlayer:
        self.dontPayPlayer.remove(self.nowPlayer.pid)
        self.nowFlowPathFinished = True
        return
      if self.LoseMoney(
          self.nowMap.payMoney,
          '当前地点需要过路费${}，你的资金不足，请点击地图上你的房屋半价出售或地皮半价抵押/出售。'.format(self.nowMap.payMoney),
          PayFinishedFunc
      ):
        PayFinishedFunc()
    else:
      self.nowFlowPathFinished = True

  def Next(self):
    """执行当前流程"""
    if not self.game.player:
      return
    if not self.nowFlowPathFinished:
      return
    if self.game.gameOver:
      return
    self.flowPaths[1] += 1
    if self.flowPaths[1] > len(config.flowPathConfig):
      self.flowPaths = [(self.flowPaths[0] + 1) % len(self.game.player), 1]
      while self.nowPlayer.fail:
        self.flowPaths = [(self.flowPaths[0] + 1) % len(self.game.player), 1]
      while self.stopPlayer.get(self.nowPlayer.pid, 0) > 0:
        self.stopPlayer[self.nowPlayer.pid] -= 1
        self.flowPaths = [(self.flowPaths[0] + 1) % len(self.game.player), 1]
      self.nowPlayer.projection.mapPos = self.nowPlayer.projection.mapPos
      self.lotteryFuncInfo = None
      self.fangChanShui = 0
      self.dontPayPlayer.clear()
      self.game.speaker.questList.clear()
      self.game.Msg('等待 {} 投掷骰子'.format(self.nowPlayer.name))
      # self.nowPlayer.panel.ClearSurfaceTemp()
    print('当前流程=', self.nowFlowPathName)
    self.nowFlowPathFinished = False
    fPFunc = getattr(self, self.nowFlowPathName)
    fPFunc()


class Roll:
  def __init__(self, game: 'Game', player: Player):
    self.game = game
    self.player = player
    self.imgSize = (35, 35)
    self.allRoll = [pygame.transform.scale(pygame.image.load('texture/p_{}.png'.format(i)).convert_alpha(), self.imgSize) for i in range(1, 7)]
    self.size = (self.imgSize[0] * 4 + 20, self.imgSize[1])
    self.rndValue = [random.randint(0, 5), random.randint(0, 5)]
    self.movePoint = 0
    self.tick = 0
    self.panel = ui.Panel(
      size=(110, 50), pos=((self.game.displaySize[0] - 110) / 2, (self.game.displaySize[1] - 50) / 2),
      childPanelDict={
        'roll0@Img': {
          'size': (50, 50),
          'pos': (0, 0)
        },
        'roll1@Img': {
          'size': (50, 50),
          'pos': (60, 0)
        },
      }
    )

  @property
  def drawSurface(self):
    return self.game.GetSurface('main')

  def Start(self):
    self.tick = time.time() + 4.3
    self.movePoint = 0
    self.game.addPlayerBtn.visible = False
    self.player.canRollBtn.visible = False
    self.game.Msg('{} 投掷骰子中……'.format(self.player.name))
    self.PlayRollSound()

  def PlayRollSound(self):
    sounds = random.sample(self.game.rollMusics, 2)
    sounds[0].play()
    self.game.AddTimer(random.uniform(0, 0.2), sounds[1].play)

  def Draw(self):
    if self.tick < 1:
      return
    nowTime = time.time()
    if self.tick - nowTime > 3:
      self.rndValue = [random.randint(0, 5), random.randint(0, 5)]
    elif self.movePoint == 0:
      print('OnRollEnd')
      if self.IsOnlyManPlayer():
        self.SetGoodRoll()
      self.OnRollEnd()
    self.panel.childPanel['roll0'].SetImg(self.allRoll[self.rndValue[0]])
    self.panel.childPanel['roll1'].SetImg(self.allRoll[self.rndValue[1]])
    self.panel.Draw(self.drawSurface)
    if nowTime >= self.tick:
      print('OnFlowPathEnd')
      self.OnFlowPathEnd()
    # surface = pygame.Surface(self.size, pygame.SRCALPHA)
    # surface.blit(self.allRoll[self.rndValue[0]], (0, 0))
    # surface.blit(self.allRoll[self.rndValue[1]], (45, 0))
    # rect = self.drawSurface.get_rect()
    # self.drawSurface.blit(surface, m2.TupleMulNumber(m2.TupleSub((rect.w, rect.h), self.size), 0.5))

  def SetGoodRoll(self):
    goodPos = []
    badPos = []
    rollList = [0, 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, 6, 2, 3, 4, 5, 6, 7, 3, 4, 5, 6, 7, 8, 4, 5, 6, 7, 8, 9, 5, 6, 7, 8, 9, 10]
    posList = []
    for roll in rollList:
      pos = (self.player.projection.mapPos + roll + 2) % len(self.game.map)
      onMap = self.game.map[pos]
      if onMap.ownerId == -1:
        posList += [roll] * 2
      elif onMap.ownerId == self.player.pid:
        if onMap.buildNumber['hotel'] == 0:
          posList += [roll] * 3
        else:
          posList += [roll] * 2
      else:
        posList += [roll]
    lastPos = random.choice(posList)
    roll1 = random.randint(max(0, lastPos - 5), min(5, lastPos))
    roll2 = lastPos - roll1
    print('set_good_pos=', roll1, roll2, lastPos)
    self.rndValue = [roll1, roll2]

  def IsOnlyManPlayer(self):
    manPlayer = []
    for player in self.game.player:
      if player.banImg.visible:
        manPlayer.append(player.pid)
    return len(manPlayer) == 1 and manPlayer[0] == self.player.pid

  def OnRollEnd(self):
    if self.game.rollNum == 0:
      self.movePoint = sum(self.rndValue) + 2
    else:
      self.movePoint = self.game.rollNum
    # self.movePoint = 12
    print('{} 扔出{}点'.format(self.player.name, self.movePoint))
    self.game.Msg('{} 扔出{}点'.format(self.player.name, self.movePoint))
    self.game.flowPath.nowFlowPathFinished = True

  def OnFlowPathEnd(self):
    self.tick = 0
    self.movePoint = 0
    self.game.addPlayerBtn.visible = True


def EventSimple():
  result = {}
  for event in pygame.event.get():
    if event.type in [pygame.FINGERDOWN, pygame.FINGERUP, pygame.FINGERMOTION]:
      continue
    result[event.type] = event
  return result


class Game:
  def __init__(self, caption='游戏标题', fps=60):
    """初始化"""
    print('游戏初始化中，请稍等……')
    st = time.time()
    pygame.init()
    print('加载游戏窗口……', time.time() - st)
    self.screen = pygame.display.set_mode()
    try:
      pygame.mixer.init()
    except Exception as e:
      print('[warn]Game.pygame.mixer.init().e=', e)
    print('加载音频资源……', time.time() - st)
    self.rollMusics = [Sound('sound/zhi_tou_zi_{}.wav'.format(i)) for i in range(5)]
    self.dingMusic = Sound('sound/ding_01.wav')
    self.moveMusic = Sound('sound/menu_move_01.wav')
    self.buildHouseMusic = Sound('sound/build_1.wav')
    self.buildHotelMusic = Sound('sound/build_2.wav')
    self.goldFallMusic = Sound('sound/gold_fall.wav')
    self.failMusic = Sound('sound/fail.wav')
    print('加载朗读者……', time.time() - st)
    self.speaker = Speaker()
    self.needSpeaker = True
    self.running = True
    self.caption = caption
    # self.screen = pygame.display.set_mode(size)
    self.displaySize = self.screen.get_size()
    pygame.display.set_caption(caption)
    self.fixTime = 0.0
    self.sellingMode = -1
    print('加载系统字体……', time.time() - st)
    self.systemFont = pygame.font.Font('font/bb.ttf', 12)
    self.clock = pygame.time.Clock()
    self.fps = fps

    # 游戏数据初始化
    print('游戏数据初始化……', time.time() - st)
    self._mapScale = config.scaleConfig.index(1.0)
    self.map = []
    print('UI初始化……', time.time() - st)
    self.mapPanel = ui.Panel(
      size=(1425, 1425), canDrag=True, pos=((self.displaySize[0] - 1425) * 0.5, (self.displaySize[1] - 1425) * 0.5)
    )
    self.rootPanel = ui.Img(
      imgPath='texture/world_1.png',
      # imgScaleZoom=False,
      size=self.displaySize
    )
    self.topUiPanel = ui.Panel(
      size=self.displaySize, layer=100
    )
    self.menuPanel = ui.Img(
      size=(300, 300), pos=((self.displaySize[0] - 300) * 0.5, (self.displaySize[1] - 300) * 0.5),
      imgPath='texture/white.png', imgColor=(0, 128, 128), visible=False,
      childPanelDict={
        'titleText@Text': {
          'text': '主菜单',
          'textFont': 'font/JingNanMaiYuanTi-2.otf',
          'textSize': 40,
          'size': (300, 40),
          'pos': (0, 10),
        },
        'addPlayerBtn@Btn': {
          'size': (100, 35),
          'pos': (100, 60),
          'text': '添加玩家',
          'textFont': 'font/JingNanMaiYuanTi-2.otf',
          'imgGrid': [1, 1, 1, 1],
          'downSound': 'sound/btndown_03.wav',
          'upFunc': self.AddPlayer
        },
        'resetPlayerPosBtn@Btn': {
          'size': (100, 35),
          'pos': (100, 105),
          'text': '重置玩家',
          'textFont': 'font/JingNanMaiYuanTi-2.otf',
          'imgGrid': [1, 1, 1, 1],
          'downSound': 'sound/btndown_03.wav',
          'upFunc': self.ResetPlayerPos
        },
        'popPlayerBtn@Btn': {
          'size': (100, 35),
          'pos': (100, 150),
          'text': '移除玩家',
          'textFont': 'font/JingNanMaiYuanTi-2.otf',
          'imgGrid': [1, 1, 1, 1],
          'downSound': 'sound/btndown_03.wav',
          'upFunc': self.PopPlayer
        },
        'sayOffOnBtn@Btn': {
          'size': (100, 35),
          'pos': (100, 195),
          'text': '开关朗读',
          'textFont': 'font/JingNanMaiYuanTi-2.otf',
          'imgGrid': [1, 1, 1, 1],
          'downSound': 'sound/btndown_03.wav',
          'upFunc': self.OnOffSay
        },
        'closeBtn@Btn': {
          'size': (100, 35),
          'pos': (100, 240),
          'text': '退出游戏',
          'textFont': 'font/JingNanMaiYuanTi-2.otf',
          'imgGrid': [1, 1, 1, 1],
          'downSound': 'sound/btndown_03.wav',
          'upFunc': self.ExitGame
        }
      }
    )
    self.menuPanel.AddChildNode('hideBtn', ui.Btn(
      pos=(274, 10), size=(16, 16), upFunc=self.HideMenu,
      defImgPath='texture/close_def.png',
      hoverImgPath='texture/close_hover.png',
      pressImgPath='texture/close_press.png',
    ))
    self.topUiPanel.AddChildNode('menuPanel', self.menuPanel)
    self.showMenuBtn = ui.Btn(
      imgGrid=[1, 1, 1, 1],
      size=(50, 25), pos=(self.displaySize[0] * 0.5 - 25, self.displaySize[1] * 0.5 - 25),
      text='菜单', upFunc=self.ShowMenu
    )
    self.topUiPanel.AddChildNode('showMenuBtn', self.showMenuBtn)
    self.topUiPanel.AddChildNode('msgTextUp', ui.Text(
      textFont='font/JingNanMaiYuanTi-2.otf', textShadow=True, textColor=(255, 255, 0),
      textSize=42, textAlign=ui.TextAlignEnum.CENTER,
      pos=(0, 20), size=(self.displaySize[0], 46),
      rot=180
    ))
    self.topUiPanel.AddChildNode('msgTextDown', ui.Text(
      textFont='font/JingNanMaiYuanTi-2.otf', textShadow=True, textColor=(255, 255, 0),
      textSize=42, textAlign=ui.TextAlignEnum.CENTER,
      pos=(0, self.displaySize[1] - 60), size=(self.displaySize[0], 46),
      rot=0
    ))
    self.topUiPanel.AddChildNode('msgTextLeft', ui.Text(
      textFont='font/JingNanMaiYuanTi-2.otf', textShadow=True, textColor=(255, 255, 0),
      textSize=42, textAlign=ui.TextAlignEnum.CENTER,
      pos=(20, 0), size=(self.displaySize[1], 46),
      rot=270
    ))
    self.topUiPanel.AddChildNode('msgTextRight', ui.Text(
      textFont='font/JingNanMaiYuanTi-2.otf', textShadow=True, textColor=(255, 255, 0),
      textSize=42, textAlign=ui.TextAlignEnum.CENTER,
      pos=(self.displaySize[0] - 60, 0), size=(self.displaySize[1], 46),
      rot=90
    ))
    self.rootPanel.AddChildNode('mapPanel', self.mapPanel)
    self.rootPanel.AddChildNode('topUiPanel', self.topUiPanel)
    self.addPlayerBtn = self.menuPanel.childPanel['addPlayerBtn']
    self.gotoMapPos = self.mapPanel.pos
    self.gotoSpeed = 0.05
    # # 禁用触屏
    # pygame.event.set_blocked(1794)
    # pygame.event.set_blocked(1792)
    # pygame.event.set_blocked(1793)
    for _mapConfig in config.mapConfig:
      self.map.append(Map(self, len(self.map), **_mapConfig))
      self.mapPanel.AddChildNode(len(self.map) - 1, self.map[-1].panel)
    self.player = []  # type: List[Player]

    self.movePos = (0, 0)
    self.mapOffset = (100, 50)

    self.allSurface = {'main': self.screen}  # type: Dict[str, pygame.Surface]
    self.lockSelect = None

    self.flowPath = FlowPath(self)
    self.timer = []

    self.gameOver = False
    self.Msg('点击棋盘中央“菜单”中的“添加玩家”')

    self.rollNum = 0
    self.tickEvent = set()

    print('游戏初始化完毕！', time.time() - st)

  def OnOffSay(self, btn):
    self.needSpeaker = not self.needSpeaker

  def PopPlayer(self, btn):
    if not self.player:
      return
    for _map in self.map:
      if _map.ownerId == self.player[-1].pid:
        _map.ownerId = -1
    self.player.pop()

  def ResetPlayerPos(self, btn):
    for _player in self.player:
      _player.panel.pos = _player.pos

  def ExitGame(self, btn):
    self.running = False

  def ShowMenu(self, btn):
    self.menuPanel.visible = True
    self.showMenuBtn.visible = False

  def HideMenu(self, btn):
    self.menuPanel.visible = False
    self.showMenuBtn.visible = True

  def GetMapPosByTitle(self, title):
    for _map in self.map:
      if _map.title == title:
        return self.map.index(_map)
    return -1

  def Msg(self, text, delayNext=0.0, delayNextFunc=None):
    print('Msg=', text, delayNext, delayNextFunc)
    self.topUiPanel.childPanel['msgTextUp'].text = text
    self.topUiPanel.childPanel['msgTextDown'].text = text
    self.topUiPanel.childPanel['msgTextLeft'].text = text
    self.topUiPanel.childPanel['msgTextRight'].text = text
    if self.needSpeaker:
      self.speaker.Say(text)
    if delayNext > 0:
      self.AddTimer(delayNext, delayNextFunc)

  def Winner(self, text):
    print('Winner', text)
    self.Msg(text)
    Sound('sound/have_winner_01.wav').play()

  def GetNoFailedPlayers(self):
    result = []
    for _player in self.player:
      if not _player.fail:
        result.append(_player)
    return result

  def GetSurface(self, surfaceName):
    return self.allSurface.get(surfaceName)

  def AddPlayer(self, btn):
    print('AddPlayer', btn.text)
    if len(self.player) > 7:
      # 玩家过多
      return
    pid = len(self.player)
    self.player.append(Player(self, pid, **config.playerInitConfig[pid]))
    if '点击棋盘中央“菜单”中的“添加玩家”' == self.topUiPanel.childPanel['msgTextUp'].text:
      self.Msg('点击玩家面板的骰子即可开始游戏！')

  @property
  def mapScale(self):
    return config.scaleConfig[self._mapScale]

  def Load(self):
    """加载游戏数据"""
    self.Doing()

  def Update(self):
    """物理更新"""
    for _map in self.map:
      _map.Update()
    self.flowPath.Next()
    self.MoveMapToGotoPos()

  def MoveMapToGotoPos(self):
    if self.sellingMode != -1:
      return
    gotoMapPos = Vector2(self.gotoMapPos)
    mapPos = Vector2(self.mapPanel.pos)
    subVector = gotoMapPos - mapPos
    distance = subVector.length()
    if distance < 1:
      self.mapPanel.pos = gotoMapPos
    else:
      # print('distance=', distance)
      newPos = subVector * self.gotoSpeed + mapPos
      self.mapPanel.pos = (newPos.x, newPos.y)

  def AddTimer(self, d, func):
    self.timer.append([time.time() + d, func])

  def SystemUpdate(self):
    """系统操作更新"""
    nowTime = time.time()
    for timer in self.timer[:]:
      if nowTime >= timer[0]:
        timer[1]()
        self.timer.remove(timer)
    # mousePos = pygame.mouse.get_pos()
    tickEvent = set()
    for event in EventSimple().values():
      # self.tickEvent.add((event.type, pygame.event.event_name(event.type)))
      if event.type in [pygame.MOUSEMOTION, pygame.MOUSEBUTTONUP, pygame.MOUSEBUTTONDOWN, pygame.FINGERUP, pygame.FINGERMOTION, pygame.FINGERDOWN]:
        ui.mousePos = event.pos
      tickEvent.add(pygame.event.event_name(event.type))
      for _player in self.player:
        if _player.panel.In(ui.mousePos, event) == ui.InResultEnum.OCCLUSION:
          return
      self.mapPanel.In(ui.mousePos, event)
      self.topUiPanel.In(ui.mousePos, event)
      if event.type == pygame.QUIT:
        self.running = False
      if event.type == pygame.KEYDOWN:
        if event.key == pygame.K_0:
          self.rollNum = 0
        elif event.key == pygame.K_1:
          self.rollNum = 1
        elif event.key == pygame.K_2:
          self.rollNum = 2
        elif event.key == pygame.K_3:
          self.rollNum = 3
        elif event.key == pygame.K_4:
          self.rollNum = 4
        elif event.key == pygame.K_5:
          self.rollNum = 5
        elif event.key == pygame.K_6:
          self.rollNum = 6
        elif event.key == pygame.K_7:
          self.rollNum = 7
        elif event.key == pygame.K_8:
          self.rollNum = 8
        elif event.key == pygame.K_9:
          self.rollNum = 9

    if tickEvent:
      self.tickEvent = tickEvent
      # print('self.tickEvent=', self.tickEvent)

  def Draw(self):
    """图像更新"""
    # self.allSurface['map'] = pygame.Surface((1425, 1425))
    # for _map in self.map:
    #   _map.Draw()
    self.rootPanel.Draw(self.screen)
    # self.mapPanel.Draw(self.screen)

    for _player in self.player:
      _player.Draw()

    # self.topUiPanel.Draw(self.screen)

  def SystemDraw(self):
    """系统图像更新"""
    text = self.systemFont.render(
      'FPS: {}  EVENT_COUNT: {}'.format(
        round(1.0 / max(0.001, self.fixTime), 1), self.tickEvent
      ), True, (0, 255, 0)
    )
    self.screen.blit(text, (10, 10))

  def Destroy(self):
    """关闭游戏时"""
    pass

  def Doing(self):
    """主进程"""
    fixStartTime = time.time()
    while self.running:
      self.screen.fill((64, 64, 64))
      nowTime = time.time()
      self.fixTime = nowTime - fixStartTime
      fixStartTime = nowTime
      self.Update()
      self.Draw()
      self.SystemUpdate()
      self.SystemDraw()
      # pygame.display.update()
      pygame.display.flip()
      self.clock.tick(self.fps)
    self.Destroy()
